#include<func.h>
//全局变量，后面捕获到退出信号回收资源；
int semid;//信号量集；
int shmid;//共享内存；
char *p;
int fdw;
int fdr;
void sigFunc(int signum);//接收到2，3信号，调用clse函数也可以用kill;
int chatA(int shmid,int semid,char*p);
int sndToA1(int shmid,int semid ,char*p,char*msg);
void closeAll();

int main()
{
  // A进程需要与B进程用管道建立聊天；1：有名管道 2：需要用select(进行监视);
  //四个进程都需要创建共享内存，A1要通过共享内存把B给A的消息显示到屏幕(用共享内存的方式);
  //创建信号量集，如果有新的消息就往共享内存中写，类似生产者
  semid = semget(2000,1,IPC_CREAT|0600);
  ERROR_CHECK(semid,-1,"shmget");
  shmid =shmget(1001,4096,IPC_CREAT|0600);
  ERROR_CHECK(shmid,-1,"shmget");
  p = (char*)shmat(shmid,NULL,0);
  ERROR_CHECK(p,(void*)-1,"shmat");
signal(SIGINT,sigFunc);
signal(SIGQUIT,sigFunc);
//开始建立通信
int ret = chatA(shmid,semid,p);
ERROR_CHECK(ret,-1,"chatA");
return 0;
}
int chatA(int shmid,int semid,char*p)
{

//建立通讯；1：需要打开通道 
fdr = open("/home/rui/warehouse/1精匠/四窗口聊天/pipe1",O_RDONLY);//绝对路径
ERROR_CHECK(fdr,-1,"fdr");
fdw = open("/home/rui/warehouse/1精匠/四窗口聊天/pipe2",O_WRONLY);
ERROR_CHECK(fdw,-1,"fdw");
setbuf(stdin,NULL);
puts("==================A==============");
char buf[1034] = {0};
//2：添加监视 3：写入共享内存；
fd_set rdset;
while (1)
{
    //设置超时
    struct timeval timeout;
    timeout.tv_sec = 500;
    timeout.tv_usec = 15000000;
    FD_ZERO(&rdset);
    FD_SET(fdr,&rdset);
    FD_SET(STDIN_FILENO,&rdset);


    
     int ret = select(fdr+1,&rdset,NULL,NULL,&timeout);//开始监听
     ERROR_CHECK(ret,-1,"select");
     if(FD_ISSET(fdr,&rdset))
     {
        memset(buf,0,sizeof(buf));
        int ret =read(fdr,buf,sizeof(buf));
        if(ret==0)
        {
         sigFunc(2);
         break;
        }
        //获取从B来的消息类型       sprintf(sstoB,"%ld %d %s",localtm,1,buf);//标注1号信息发送给B；
        int type = 0;
        sscanf(buf,"%*d %d",&type);//读取消息的类型；
         int snd_ret = 0;//实际发送消息的字节数量；
         switch(type)
         {
         case 1:
             // 如果是1号信息，通过共享内存直接把消息发送给A1；
             {
                 snd_ret = sndToA1(shmid, semid, p, buf);
                 ERROR_CHECK(snd_ret, -1, "sndToA1");
                 break;
             }
         case 2:
             // 如果是B发送过来的2号信息，关闭所有窗口==
             // 向A1发送一个空的2号信息让A1自己退出，然后自己退出；
             {
                 sigFunc(2);
                 exit(0);
             }
         }
     }
if(FD_ISSET(STDIN_FILENO,&rdset))
{
   //如果标准输入准备就绪，读取标准输入区的数据，标记为3号信息，发送给A和B
   time_t localtm;
   time(&localtm);
   localtm +=8*3600;
   memset(buf,0,sizeof(buf));
   int  ret = read(STDERR_FILENO,buf,sizeof(buf));
   if(ret ==0)
   {
    puts("I quite.");
    break;
   }
char sstoA1[1024] = {0};//用来拼接数据，发送给A1的数据
char sstoB[1024] = {0};//用来拼接数据，发送给B的数据；
sprintf(sstoA1,"%ld %d %s",localtm,3,buf);//标注3号信息发送给A1
sprintf(sstoB,"%ld %d %s",localtm,1,buf);//标注1号信息发送给B；
sndToA1(shmid,semid,p,sstoA1);//发送给A1；
write(fdw,sstoB,sizeof(sstoB));//通过管道发送给B；


}

}
close(fdr);
close(fdw);
return 1;
}


int sndToA1(int shmid,int semid ,char*p,char*msg)
{
//使用共享内存和信号量给A1传递信息；
//信号量集的操作，如果有新的消息就往共享内存中写，类似生产者；
struct sembuf V;
V.sem_flg =SEM_UNDO;
V.sem_op = 1;
V.sem_num = 0;
semop(semid,&V,1);
memcpy(p,msg,strlen(msg));//向共享内存中写信息；
}
void closeAll()
{
    //根据共享内存和信号量集合的表示符号，关闭并删除他们；
    write(fdw,"0 2 0",5);
    shmdt(p);
    shmctl(shmid,IPC_RMID,NULL);
    semctl(semid,IPC_RMID,0);
    
    exit(0);
}

void sigFunc(int signum)
{
    printf("Bye Bye\n");
    //捕捉2号和三号信息，发送给A1然后调用closeALL;
    sndToA1(shmid,semid,p,"0 2 0");//发送给A1；
    usleep(500);
    closeAll();
}